﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Threading.Tasks;
using WxLib.Core.Util;
using WxLib.Core.RequestHandlers;
using WxLib.Core.Serializers;

namespace WxLib.Common
{
    /// <summary>
    /// 微信常用接口
    /// </summary>
    public class WxCommonApi : WxLibApiBase
    {
        private static volatile WxCommonApi _instance = null;
        private static readonly object lockObject = new object();
        private const string DefaultHost = "https://api.weixin.qq.com";
        private const string DefaultApiPath = "/cgi-bin";

        /// <summary>
        /// 保存三方用户的AccessToken集合
        /// </summary>
        private Dictionary<string, WxAccessTokenResponse> accessTokenDictionary = new Dictionary<string, WxAccessTokenResponse>();
        private AsyncLocker locker = new AsyncLocker();
        private DateTime? lastReloadDate;//用于控制请求太频繁
        private bool reload = true;

        /// <summary>
        /// 获取或设置用于执行请求的RequestHandler
        /// </summary>
        public IPostRequestHandler RequestHandler;

        /// <summary>
        /// 构造函数
        /// </summary>
        public WxCommonApi()
            : base(DefaultHost, new RequestHandler(new JsonSerializer()))
        {
            ApiPath = DefaultApiPath;
            RequestHandler = new RequestHandler(new JsonSerializer());
        }

        /// <summary>
        /// 指示是否重新获取获取access_token
        /// </summary>
        public bool Reload
        {
            get { return reload; }
            set
            {
                if (value && (!lastReloadDate.HasValue || lastReloadDate.Value.AddSeconds(60) < DateTime.Now))
                {
                    reload = value;
                }
            }
        }

        /// <summary>
        /// 获取常用接口实例
        /// </summary>
        /// <returns></returns>
        public static WxCommonApi Instance()
        {
            if (_instance == null)
            {
                lock (lockObject)
                {
                    if (_instance == null)
                    {
                        _instance = new WxCommonApi();
                    }
                }
            }
            return _instance;
        }

        /// <summary>
        /// 获取access_token(根据微信接口返回的过期时间缓存)
        /// </summary>
        /// <param name="appid">第三方用户唯一凭证</param>
        /// <param name="secret">第三方用户唯一凭证密钥，即appsecret</param>
        /// <param name="grant_type">获取access_token填写client_credential</param>
        /// <returns></returns>
        public WxAccessTokenResponse GetAccessToken(string appid, string secret, string grant_type = "client_credential")
        {
            return GetAccessTokenAsync(appid, secret, grant_type).Result;
        }


        /// <summary>
        /// 获取access_token(根据微信接口返回的过期时间缓存)
        /// </summary>
        /// <param name="appid">第三方用户唯一凭证</param>
        /// <param name="secret">第三方用户唯一凭证密钥，即appsecret</param>
        /// <param name="grant_type">获取access_token填写client_credential</param>
        /// <returns></returns>
        public WxAccessTokenResponse GetAccessTokenQeg(string appid, string secret, string grant_type = "client_credential")
        {
            return GetAccessTokenAsyncQeg(appid, secret, grant_type).Result;
        }


        /// <summary>
        /// 获取access_token漫登(根据微信接口返回的过期时间缓存)
        /// </summary>
        /// <param name="appid">第三方用户唯一凭证</param>
        /// <param name="secret">第三方用户唯一凭证密钥，即appsecret</param>
        /// <param name="grant_type">获取access_token填写client_credential</param>
        /// <returns></returns>
        public WxAccessTokenResponse GetAccessTokenMd(string appid, string secret, string grant_type = "client_credential")
        {
            return GetAccessTokenMdAsync(appid, secret, grant_type).Result;
        }


        /// <summary>
        /// 异步获取access_token(根据微信接口返回的过期时间缓存)  【企二哥】
        /// </summary>
        /// <param name="appid">第三方用户唯一凭证</param>
        /// <param name="secret">第三方用户唯一凭证密钥，即appsecret</param>
        /// <param name="grant_type">获取access_token填写client_credential</param>
        /// <returns></returns>
        public async Task<WxAccessTokenResponse> GetAccessTokenAsyncQeg(string appid, string secret, string grant_type = "client_credential")
        {
            WxAccessTokenResponse accessTokenResponse = null;
            string relPath = "/token?grant_type=" + grant_type + "&appid=" + appid + "&secret=" + secret;
            accessTokenResponse = await requestAsync<WxAccessTokenResponse>(relPath).ConfigureAwait(false);
            return accessTokenResponse;
        }

        /// <summary>
        /// 异步获取access_token(根据微信接口返回的过期时间缓存)  【企牛】
        /// </summary>
        /// <param name="appid">第三方用户唯一凭证</param>
        /// <param name="secret">第三方用户唯一凭证密钥，即appsecret</param>
        /// <param name="grant_type">获取access_token填写client_credential</param>
        /// <returns></returns>
        public async Task<WxAccessTokenResponse> GetAccessTokenAsync(string appid, string secret, string grant_type = "client_credential")
        {
            WxAccessTokenResponse accessTokenResponse = null;

            //由于前后端都调用 token，单例存储之后。会导致两边不一样
            var result = await RequestHandler.PostRequestAsync<ApiResultWx>(new Uri("https://www.qierge.com/api/hm/Access_token"), "{\"Key\": \"access_token\"}");
            if (result.success && !string.IsNullOrEmpty(result.msg))
            {
                var sp = result.msg.Split('☆');
                if (sp.Length == 2)
                {
                    try
                    {
                        var expirationTime = Convert.ToDateTime(sp[1]);
                        //如果过期时间大于当前系统时间，那么说明这个token还未过期
                        if (expirationTime > DateTime.Now)
                        {
                            accessTokenResponse = new WxAccessTokenResponse();
                            accessTokenResponse.access_token = sp[0];
                            accessTokenResponse.expires_in = 7200;//TODO:这个暂时没有啥卵用
                            return accessTokenResponse;
                        }
                    }
                    catch (Exception ex)
                    {

                    }
                }
            }

            ////判断下时间，若缓存中的时间小于当前时间，则重新开始
            //if (!lastReloadDate.HasValue || lastReloadDate < DateTime.Now)
            //{
            //    Reload = true;
            //}
            //if (accessTokenDictionary.ContainsKey(appid) && !accessTokenDictionary[appid].IsExpired() && !Reload)
            //    return accessTokenDictionary[appid];

            using (var releaser = await locker.LockAsync())
            {
                //if (accessTokenDictionary.ContainsKey(appid) && !Reload)
                //    return accessTokenDictionary[appid];

                string relPath = "/token?grant_type=" + grant_type + "&appid=" + appid + "&secret=" + secret;
                accessTokenResponse = await requestAsync<WxAccessTokenResponse>(relPath).ConfigureAwait(false);

                if (accessTokenResponse != null)
                {
                    string tokenNow = accessTokenResponse.access_token + "☆" + DateTime.Now.AddHours(1).ToString("yyyy-MM-dd HH:mm:ss");
                    var upResult = await RequestHandler.RequestAsync<ApiResultWx>(new Uri("https://www.qierge.com/api/hm/UpdateAccess_token?value=" + tokenNow));
                    if (upResult.success && upResult.data != null)
                    {
                        //看看是否调用成功

                    }

                    accessTokenDictionary[appid] = accessTokenResponse;
                    reload = false;
                    lastReloadDate = DateTime.Now.AddHours(1);//本来一小时，接口时候两小时
                }
                return accessTokenResponse;
            }

        }


        /// <summary>
        /// 异步获取access_token(根据微信接口返回的过期时间缓存)  【企牛】
        /// </summary>
        /// <param name="appid">第三方用户唯一凭证</param>
        /// <param name="secret">第三方用户唯一凭证密钥，即appsecret</param>
        /// <param name="grant_type">获取access_token填写client_credential</param>
        /// <returns></returns>
        public async Task<WxAccessTokenResponse> GetAccessTokenMdAsync(string appid, string secret, string grant_type = "client_credential")
        {
            WxAccessTokenResponse accessTokenResponse = null;

            //由于前后端都调用 token，单例存储之后。会导致两边不一样
            var result = await RequestHandler.PostRequestAsync<ApiResultWx>(new Uri("https://www.qierge.com/api/hm/Access_tokenMd"), "{}");
            if (result.success && !string.IsNullOrEmpty(result.msg))
            {
                var sp = result.msg.Split('☆');
                if (sp.Length == 2)
                {
                    try
                    {
                        var expirationTime = Convert.ToDateTime(sp[1]);
                        //如果过期时间大于当前系统时间，那么说明这个token还未过期
                        if (expirationTime > DateTime.Now)
                        {
                            accessTokenResponse = new WxAccessTokenResponse();
                            accessTokenResponse.access_token = sp[0];
                            accessTokenResponse.expires_in = 7200;//TODO:这个暂时没有啥卵用
                            return accessTokenResponse;
                        }
                    }
                    catch (Exception ex)
                    {

                    }
                }
            }

            ////判断下时间，若缓存中的时间小于当前时间，则重新开始
            //if (!lastReloadDate.HasValue || lastReloadDate < DateTime.Now)
            //{
            //    Reload = true;
            //}
            //if (accessTokenDictionary.ContainsKey(appid) && !accessTokenDictionary[appid].IsExpired() && !Reload)
            //    return accessTokenDictionary[appid];

            using (var releaser = await locker.LockAsync())
            {
                //if (accessTokenDictionary.ContainsKey(appid) && !Reload)
                //    return accessTokenDictionary[appid];

                string relPath = "/token?grant_type=" + grant_type + "&appid=" + appid + "&secret=" + secret;
                accessTokenResponse = await requestAsync<WxAccessTokenResponse>(relPath).ConfigureAwait(false);

                if (accessTokenResponse != null)
                {
                    string tokenNow = accessTokenResponse.access_token + "☆" + DateTime.Now.AddHours(1).ToString("yyyy-MM-dd HH:mm:ss");
                    var upResult = await RequestHandler.RequestAsync<ApiResultWx>(new Uri("https://www.qierge.com/api/hm/UpdateAccess_tokenMd?value=" + tokenNow));
                    if (upResult.success && upResult.data != null)
                    {
                        //看看是否调用成功

                    }

                    accessTokenDictionary[appid] = accessTokenResponse;
                    reload = false;
                    lastReloadDate = DateTime.Now.AddHours(1);//本来一小时，接口时候两小时
                }
                return accessTokenResponse;
            }

        }



        /// <summary>
        /// 获取access_token(根据微信接口返回的过期时间缓存)
        /// </summary>
        /// <param name="appid">第三方用户唯一凭证</param>
        /// <param name="secret">第三方用户唯一凭证密钥，即appsecret</param>
        /// <param name="grant_type">获取access_token填写client_credential</param>
        /// <returns></returns>
        public WxAccessTokenResponse GetAccessTokenXcx(string appid, string secret, string grant_type = "client_credential")
        {
            return GetAccessTokenAsyncxcx(appid, secret, grant_type).Result;
        }

        /// <summary>
        /// 异步获取access_token(根据微信接口返回的过期时间缓存)
        /// </summary>
        /// <param name="appid">第三方用户唯一凭证</param>
        /// <param name="secret">第三方用户唯一凭证密钥，即appsecret</param>
        /// <param name="grant_type">获取access_token填写client_credential</param>
        /// <returns></returns>
        public async Task<WxAccessTokenResponse> GetAccessTokenAsyncxcx(string appid, string secret, string grant_type = "client_credential")
        {
            WxAccessTokenResponse accessTokenResponse = null;
            ////判断下时间，若缓存中的时间小于当前时间，则重新开始
            //if (!lastReloadDate.HasValue || lastReloadDate < DateTime.Now)
            //{
            //    Reload = true;
            //}
            //if (accessTokenDictionary.ContainsKey(appid) && !accessTokenDictionary[appid].IsExpired() && !Reload)
            //    return accessTokenDictionary[appid];

            using (var releaser = await locker.LockAsync())
            {
                //if (accessTokenDictionary.ContainsKey(appid) && !Reload)
                //    return accessTokenDictionary[appid];

                string relPath = "/token?grant_type=" + grant_type + "&appid=" + appid + "&secret=" + secret;
                accessTokenResponse = await requestAsync<WxAccessTokenResponse>(relPath).ConfigureAwait(false);

                if (accessTokenResponse != null)
                {
                    accessTokenDictionary[appid] = accessTokenResponse;
                    reload = false;
                    lastReloadDate = DateTime.Now.AddHours(1);//本来一小时，接口时候两小时
                }
                return accessTokenResponse;
            }

        }

    }
}
